package com.ideal.blockchain.controller.block;


import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.protobuf.InvalidProtocolBufferException;
import com.ideal.blockchain.config.ChannelContext;
import com.ideal.blockchain.dto.request.BlockDto;
import com.ideal.blockchain.dto.response.ResultInfo;
import com.ideal.blockchain.enums.ResponseCodeEnum;
import com.ideal.blockchain.service.block.BlockService;
import com.ideal.blockchain.service.block.UserService;
import com.ideal.blockchain.utils.Utils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Hex;
import org.hyperledger.fabric.sdk.BlockInfo;
import org.hyperledger.fabric.sdk.exception.InvalidArgumentException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;


import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Date;

/**
 * @author: Jackie
 * @date: 2021/01/10 10:03
 */
@RestController
@Slf4j
@RequestMapping("/blockInfo")
public class BlockController {

    @Autowired
    private BlockService blockService;

    @Autowired
    private UserService userService;

    /**
     *
     * 根据区块高度查询 区块信息
     */
    @RequestMapping(value = "/withNum", method = RequestMethod.POST)
    @ResponseBody
    public ResultInfo blockInfoByNum(@RequestBody BlockDto blockDto) throws Exception {
        try {
            if (StringUtils.isEmpty(blockDto.getUserName())) {
                return new ResultInfo(ResponseCodeEnum.FAILURE, "请输入用户名：userName");
            }
            if (StringUtils.isEmpty(blockDto.getPassWord())) {
                return new ResultInfo(ResponseCodeEnum.FAILURE, "请输入对应的密码");
            }
            if (StringUtils.isEmpty(blockDto.getPeerWithOrg())) {
                return new ResultInfo(ResponseCodeEnum.FAILURE, "请输入查询的组织名称:peerWithOrg");
            }
            if (StringUtils.isEmpty(blockDto.getChannelName())) {
                return new ResultInfo(ResponseCodeEnum.FAILURE, "请输入查询的通道名称:channelName");
            }
            if (StringUtils.isEmpty(blockDto.getNumber())) {
                return new ResultInfo(ResponseCodeEnum.FAILURE, "请输入要查询的区块高度:number");
            }
            log.debug(blockDto.getUserName());

            String result = userService.loadUserFromPersistence(blockDto.getUserName(), blockDto.getPassWord(), blockDto.getPeerWithOrg());
            if (result == "Successfully loaded member from persistence") {
                BlockInfo blockInfo = blockService.blockChainInfoByNumber(blockDto.getUserName(), blockDto.getPeerWithOrg(), blockDto.getChannelName(), blockDto.getNumber());
                if (blockInfo==null){
                    return new ResultInfo(ResponseCodeEnum.FAILURE, "查询区块高度不能高于当前链高度");
                }
                JSONObject jsonObject = execBlockInfo(blockInfo);
                return new ResultInfo(ResponseCodeEnum.SUCCESS, jsonObject);
            } else {
                return new ResultInfo(ResponseCodeEnum.FAILURE, "查询区块出错");
            }

        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage());
            return new ResultInfo(ResponseCodeEnum.FAILURE, e.getMessage());
        } finally {
            ChannelContext.clear();
        }
    }

    /**
     *
     * 根据区块交易ID查询 区块信息
     */

    @RequestMapping(value = "/withTxId", method = RequestMethod.POST)
    @ResponseBody
    public ResultInfo blockInfoByTxId(@RequestBody BlockDto blockDto) throws Exception {
        try {
            if (StringUtils.isEmpty(blockDto.getUserName())) {
                return new ResultInfo(ResponseCodeEnum.FAILURE, "请输入用户名");
            }
            if (StringUtils.isEmpty(blockDto.getPassWord())) {
                return new ResultInfo(ResponseCodeEnum.FAILURE, "请输入用户对应的密码");
            }
            if (StringUtils.isEmpty(blockDto.getPeerWithOrg())) {
                return new ResultInfo(ResponseCodeEnum.FAILURE, "请输入要查询的组织名,如peerOrg1");
            }
            if (StringUtils.isEmpty(blockDto.getChannelName())) {
                return new ResultInfo(ResponseCodeEnum.FAILURE, "请输入要查询的通道名称");
            }
            if (StringUtils.isEmpty(blockDto.getTxId())) {
                return new ResultInfo(ResponseCodeEnum.FAILURE, "请输入要查询的交易ID");
            }
            log.debug(blockDto.getUserName());

            String result = userService.loadUserFromPersistence(blockDto.getUserName(), blockDto.getPassWord(), blockDto.getPeerWithOrg());
            if (result == "Successfully loaded member from persistence") {
                BlockInfo blockInfo = blockService.blockChainInfoByTxId(blockDto.getUserName(), blockDto.getPeerWithOrg(), blockDto.getChannelName(), blockDto.getTxId());
                if (blockInfo==null){
                    return new ResultInfo(ResponseCodeEnum.FAILURE, "请输入正确的交易ID");
                }
                JSONObject jsonObject = execBlockInfo(blockInfo);

                return new ResultInfo(ResponseCodeEnum.SUCCESS, jsonObject);
            } else {
                return new ResultInfo(ResponseCodeEnum.FAILURE, "查询交易ID出错");
            }

        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage());
            return new ResultInfo(ResponseCodeEnum.FAILURE, e.getMessage());
        } finally {
            ChannelContext.clear();
        }
    }




    private JSONObject execBlockInfo(BlockInfo blockInfo) throws IOException, InvalidArgumentException {
        final long blockNumber = blockInfo.getBlockNumber();
        JSONObject blockJson = new JSONObject();
        blockJson.put("blockNumber", blockNumber);
        blockJson.put("dataHash", Hex.encodeHexString(blockInfo.getDataHash()));
        blockJson.put("transActionsMetaData",Hex.encodeHexString(blockInfo.getTransActionsMetaData()));
//        blockJson.put("BlockData", blockInfo.getBlock().getData().getData(0).toStringUtf8());
//        blockJson.put("BlockData", blockInfo.getBlock().getData().getData(0).toString());
        blockJson.put("previousHashID", Hex.encodeHexString(blockInfo.getPreviousHash()));
//        blockJson.put("calculatedBlockHash", Hex.encodeHexString(SDKUtils.calculateBlockHash(org.getClient(), blockNumber, blockInfo.getPreviousHash(), blockInfo.getDataHash())));

        blockJson.put("envelopeCount", blockInfo.getEnvelopeCount());
        log.debug("blockNumber = " + blockNumber);
        log.debug("data hash: " + Hex.encodeHexString(blockInfo.getDataHash()));
        log.debug("previous hash id: " + Hex.encodeHexString(blockInfo.getPreviousHash()));
//        log.debug("calculated block hash is " + Hex.encodeHexString(SDKUtils.calculateBlockHash(org.getClient(), blockNumber, blockInfo.getPreviousHash(), blockInfo.getDataHash())));
        log.debug("block number " + blockNumber + " has " + blockInfo.getEnvelopeCount() + " envelope count:");
        blockJson.put("envelopes", getEnvelopeJsonArray(blockInfo, blockNumber));
        return blockJson;
    }

    private JSONArray getEnvelopeJsonArray(BlockInfo blockInfo, long blockNumber) throws UnsupportedEncodingException, InvalidProtocolBufferException {
        JSONArray envelopeJsonArray = new JSONArray();
        for (BlockInfo.EnvelopeInfo info : blockInfo.getEnvelopeInfos()) {
            JSONObject envelopeJson = new JSONObject();
            envelopeJson.put("channelId", info.getChannelId());
            envelopeJson.put("transactionID", info.getTransactionID());
            envelopeJson.put("validationCode", info.getValidationCode());
            envelopeJson.put("timestamp", Utils.parseDateFormat(new Date(info.getTimestamp().getTime())));
            envelopeJson.put("type", info.getType());
//            envelopeJson.put("createId", info.getCreator().getId());
            envelopeJson.put("createMSPID", info.getCreator().getMspid());
            envelopeJson.put("isValid", info.isValid());
            envelopeJson.put("nonce", Hex.encodeHexString(info.getNonce()));

            log.debug("channelId = " + info.getChannelId());
            log.debug("nonce = " + Hex.encodeHexString(info.getNonce()));
//            log.debug("createId = " + info.getCreator().getId());
            log.debug("createMSPID = " + info.getCreator().getMspid());
            log.debug("isValid = " + info.isValid());
            log.debug("transactionID = " + info.getTransactionID());
            log.debug("validationCode = " + info.getValidationCode());
            log.debug("timestamp = " + Utils.parseDateFormat(new Date(info.getTimestamp().getTime())));
            log.debug("type = " + info.getType());

            if (info.getType() == BlockInfo.EnvelopeType.TRANSACTION_ENVELOPE) {
                BlockInfo.TransactionEnvelopeInfo txeInfo = (BlockInfo.TransactionEnvelopeInfo) info;
                JSONObject transactionEnvelopeInfoJson = new JSONObject();
                int txCount = txeInfo.getTransactionActionInfoCount();
                transactionEnvelopeInfoJson.put("txCount", txCount);
                transactionEnvelopeInfoJson.put("isValid", txeInfo.isValid());
                transactionEnvelopeInfoJson.put("validationCode", txeInfo.getValidationCode());

                log.debug("Transaction number " + blockNumber + " has actions count = " + txCount);
                log.debug("Transaction number " + blockNumber + " isValid = " + txeInfo.isValid());
                log.debug("Transaction number " + blockNumber + " validation code = " + txeInfo.getValidationCode());

//                transactionEnvelopeInfoJson.put("transactionActionInfoArray", getTransactionActionInfoJsonArray(txeInfo, txCount));
                envelopeJson.put("transactionEnvelopeInfo", transactionEnvelopeInfoJson);
            }
            envelopeJsonArray.add(envelopeJson);
        }
        return envelopeJsonArray;
    }


}
